package edge

import (
	"fmt"
	"iot-base/common/tunnel/morrior"
	"iot-base/common/tunnel/protocol"
	"net"
	"sync"
	"time"

	"github.com/xtaci/kcp-go/v5"
)

type Connection struct {
	//closechan chan bool
	//timer     *time.Timer
	id      string
	conn    *net.UDPConn
	sevAddr *net.UDPAddr
}

func (pConnection *Connection) sendHB() {

	var buff [200]byte
	var header protocol.Header
	header.Ptype = protocol.HB
	header.Body = []byte(pConnection.id)
	len, _ := (&header).ToBuffer(buff[0:])

	slen, err := pConnection.conn.WriteToUDP(buff[0:len], pConnection.sevAddr)
	fmt.Println("send hb-----------", pConnection.id, pConnection.sevAddr, len, err, slen)
}

func (pConnection *Connection) recvHeader() (*protocol.Header, *net.UDPAddr) {
	var buff [200]byte
	nLen, pAddr, err := pConnection.conn.ReadFromUDP(buff[:100])
	fmt.Println("recv----", nLen, pAddr, err)
	if err == nil {
		pHeader, _ := protocol.GetHeader(buff[0:], nLen)
		fmt.Println("---------------!!!!!!!!!! get data from remote", pHeader)
		return pHeader, pAddr

	}
	return nil, nil
}

//beginKCP 发起kcp连接

func (pConnection *Connection) check() bool {
	pConnection.conn.SetReadDeadline(time.Now().Add(time.Duration(10) * time.Second))
	pCurHeader, pAddr := pConnection.recvHeader()
	if pCurHeader == nil || pAddr == nil {
		fmt.Println("check----recv------timeout-------return")
		return true
	}
	if pCurHeader != nil {
		sid := string(pCurHeader.Body)
		if pCurHeader.Ptype == protocol.PROBE && sid == pConnection.id {
			//beginKCP------------------------------->
			fmt.Println("---------------!!!!!!!!!! get probe from remote", pAddr)
			var proHeader protocol.Header
			proHeader.Ptype = protocol.BEGIN_KCP
			proHeader.Seq = pCurHeader.Seq
			proHeader.Body = []byte(pConnection.id)
			var buf [100]byte
			nLen, _ := (&proHeader).ToBuffer(buf[:])
			pConnection.conn.WriteToUDP(buf[:nLen], pAddr)
			localAddr := pConnection.conn.LocalAddr()
			pConnection.conn.Close()
			time.Sleep(time.Second)
			/*key := pbkdf2.Key([]byte("demo pass"), []byte("demo salt"), 1024, 32, sha1.New)
			block, _ := kcp.NewAESBlockCrypt(key)*/
			//pConnection.closechan <- true

			//fmt.Println("kcp SetReadDeadline----------4--------finished")
			//等待心跳进程彻底退出
			//time.Sleep(3 * time.Second)
			//pConnection.conn.SetReadDeadline(time.Now().Add(time.Duration(10) * time.Hour))

			//func NewConn3(convid uint32, raddr net.Addr, block BlockCrypt, dataShards, parityShards int, conn net.PacketConn)
			//pConnection.conn.SetDeadline(time.Unix(0, 0))
			udpLocalAddr, err := net.ResolveUDPAddr("udp4", localAddr.String())
			fmt.Println("*************udplocaladdr,err********edge:", udpLocalAddr, err, pAddr)
			conn, errl := net.ListenUDP("udp4", udpLocalAddr)
			fmt.Println("*************udplocaladdr,err********edge:", udpLocalAddr, errl)
			//if client, err := kcp.NewConn2(pAddr, nil, 10, 3, pConnection.conn); err == nil {
			//buf := make([]byte, 1024)
			//pConnection.closechan <- true
			//client.SetDeadline(time.Now().Add(time.Duration(10) * time.Second))
			if client, err := kcp.NewConn2(pAddr, nil, 10, 3, conn); err == nil {
				var wg sync.WaitGroup
				wg.Add(1)
				//client.Read()
				fmt.Println("---------kcp connect--------", err, pAddr)
				client.SetDeadline(time.Now().Add(time.Duration(10) * time.Hour))
				morrior.BeginEdge(&wg, client)
				wg.Wait()

			} else {
				fmt.Println("newconn3 error", err)
			}
			//pConnection.closechan <- true
			return false
		}
	}
	return true
}
func (pConnection *Connection) run() {
	for {
		fmt.Println("begin-----------recv----")
		pConnection.recv()
	}
}
func (pConnection *Connection) recv() {

	//pConnection.conn.SetReadDeadline(time.Now().Add(time.Duration(300) * time.Second))
	//fmt.Println("kcp SetReadDeadline----------5--------finished")
	pHeader, _ := pConnection.recvHeader()
	if pHeader == nil {
		//pConnection.closechan <- true
		fmt.Println("conn SetReadDeadline----------300s--------timeout")
		return
	}
	if pHeader.Ptype == protocol.BEGIN {

		addr := string(pHeader.Body)
		fmt.Println("begin probe ------addr--------------", pHeader, addr)
		udpAddr, err := net.ResolveUDPAddr("udp4", addr)
		if err == nil {
			var buff [200]byte
			//pHeader.Ptype = protocol.PROBE
			pHeader.Body = []byte(pConnection.id)
			slen, _ := pHeader.ToBuffer(buff[0:100])
			kcpConn, _ := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
			var kcpConnection Connection
			kcpConnection.conn = kcpConn
			for i := 0; i < 5; i++ {
				buff[0] = protocol.PROBE
				nLen, err := kcpConn.WriteToUDP(buff[0:slen], udpAddr)
				fmt.Println("send data-----", nLen, err, udpAddr)
				buff[0] = protocol.WAKEUP_RES
				nLen, err = kcpConn.WriteToUDP(buff[0:slen], pConnection.sevAddr)
				fmt.Println("send data--2---", nLen, err, pConnection.sevAddr)

			}
			//time.Sleep(300 * time.Millisecond)
			kcpConnection.id = pConnection.id
			go kcpConnection.check()
		}

	}

}

func BeginTunnel(svrip string, port int, id string, name string) {
	pConnection := &Connection{
		//closechan: make(chan bool),
		//timer:     time.NewTimer(30 * time.Second),
		id: id + ":::" + name,
	}
	svr := fmt.Sprintf("%s:%d", svrip, port)
	udpAddr, _ := net.ResolveUDPAddr("udp4", svr)
	conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	fmt.Println("listen-----", pConnection.id, udpAddr, err)
	if err == nil {
		pConnection.conn = conn
		pConnection.sevAddr = udpAddr
		pConnection.sendHB()
		go pConnection.run()
		for {
			timer := time.NewTimer(30 * time.Second)
			timer.Reset(30 * time.Second)
			select {
			case <-timer.C:
				pConnection.sendHB()

			}
		}

	}
}
